﻿#region Using

using System;
using System.Reflection;
using Commons.Extension;
using SAPbouiCOM;
using SapFramework.Application;
using SapFramework.Application.Connections;
using SapFramework.Interface.Controls.Menus.Base;
using Form = SapFramework.Interface.Controls.Forms.Base.Form;

#endregion

namespace SapFramework.Interface.Events
{
    /// <summary>
    /// Define e registra as funções que realizam o tratamento de eventos dentro da UI API do SAP Business One.
    /// </summary>
    public class EventHandler : IEventHandler
    {
        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="aAddOn">AddOn do SBO desenvolvido pelo PadaOne.</param>
        public EventHandler( AddOn aAddOn )
        {
            Commons.Diagnostics.Contract.Requires.NotNull( () => aAddOn );
            AddOn = aAddOn;
            Commons.Diagnostics.Contract.Requires.NotNull( () => AddOn.Forms );
            Commons.Diagnostics.Contract.Requires.NotNull( () => AddOn.Connection );
            Commons.Diagnostics.Contract.Requires.NotNull( () => AddOn.Connection.Interface );
            SapApplication = AddOn.Connection.Interface;

            Listener = new EventListener();

            AddEvents();
            AddFilters();
        }

        /// <summary>
        /// Referência ao AddOn que se está mapeando os eventos.
        /// </summary>
        public AddOn AddOn { get; private set; }
        /// <summary>
        /// Referência para a aplicação da UI.
        /// </summary>
        /// <remarks>
        /// É mais pra facilitar a chamada deste cara mesmo...
        /// </remarks>
        public SAPbouiCOM.Application SapApplication { get; private set; }
        /// <summary>
        /// Realiza o mapeamento dos eventos que deverão ser chamados.
        /// </summary>
        public EventListener Listener { get; private set; }
        /// <summary>
        /// Quando for excluir o objeto, remove antes os registros da aplicação.
        /// </summary>
        /// <remarks>
        /// Destrutores são automáticamente invocados pela framework <c>dotNet</c>.
        /// </remarks>
        ~EventHandler()
        {
            SubEvents();
        }

        /// <summary>
        /// Registra as funções de eventos 
        /// </summary>
        protected void AddEvents()
        {
            SapApplication.AppEvent += ApplicationEventHandler;
            SapApplication.MenuEvent += MenuEventHandler;
            SapApplication.FormDataEvent += FormDataEventHandler;
            SapApplication.ItemEvent += ItemEventHandler;
            SapApplication.RightClickEvent += RightClickEventHandler;
            SapApplication.PrintEvent += PrintEventHandler;
            SapApplication.ReportDataEvent += ReportDataEventHandler;
            SapApplication.ProgressBarEvent += ProgressBarEventHandler;
            SapApplication.StatusBarEvent += StatusBarEventHandler;
            SapApplication.LayoutKeyEvent += LayoutKeyEventHandler;
            SapApplication.WidgetEvent += WidgetEventHandler;
        }
        /// <summary>
        /// Registra os filtros para as funções de formulários que serão capturados.
        /// </summary>
        protected void AddFilters()
        {
            EventFilters lEventFilters = Listener.Filters.BuildSboEventFilters();

            SapApplication.SetFilter( lEventFilters );
        }

        /// <summary>
        /// Desregistra as funções de eventos 
        /// </summary>
        protected void SubEvents()
        {
            if ( SapApplication == null )
                return;

            SapApplication.AppEvent -= ApplicationEventHandler;
            SapApplication.MenuEvent -= MenuEventHandler;
            SapApplication.FormDataEvent -= FormDataEventHandler;
            SapApplication.ItemEvent -= ItemEventHandler;
            SapApplication.RightClickEvent -= RightClickEventHandler;
            SapApplication.PrintEvent -= PrintEventHandler;
            SapApplication.ReportDataEvent -= ReportDataEventHandler;
            SapApplication.ProgressBarEvent -= ProgressBarEventHandler;
            SapApplication.StatusBarEvent -= StatusBarEventHandler;
            SapApplication.LayoutKeyEvent -= LayoutKeyEventHandler;
            SapApplication.WidgetEvent -= WidgetEventHandler;
        }

        #region Métodos de auxílio

        /// <summary>
        /// Gerencia os eventos que podem partir de um formulário e seus respectivos itens.
        /// </summary>
        /// <param name="aEventInfo">Contém informações sobre o evento.</param>
        /// <param name="aItemUniqueId"></param>
        /// <returns></returns>
        protected bool HandleFormEvent( dynamic aEventInfo, string aItemUniqueId = null )
        {
            Form lForm = AddOn.Forms[aEventInfo.FormUID];

            if ( lForm == null && (BoEventTypes)aEventInfo.EventType == BoEventTypes.et_FORM_LOAD && aEventInfo.BeforeAction )
            {
                lForm = AddOn.Forms.Add( (string)aEventInfo.FormUID, (string)aEventInfo.FormTypeEx );
            }
            else if ( lForm == null )
            {
                return true;
            }

            BoEventTypes lEventType = (BoEventTypes)aEventInfo.EventType;

            if ( lEventType == BoEventTypes.et_FORM_CLOSE && aEventInfo.BeforeAction == false )
                AddOn.Forms.Remove( lForm );

            string lItemUniqueId = aItemUniqueId ?? aEventInfo.ItemUID;

            MethodInfo lMethodInfo = Listener.FormEvents[aEventInfo.FormTypeEx, lItemUniqueId, lEventType, aEventInfo.BeforeAction];

            if ( lMethodInfo == null )
                return true;

            object lReturn = lMethodInfo.Invoke( lForm, new[] {aEventInfo} );

            return lMethodInfo.ReturnType == typeof(bool) ? (bool)lReturn : true;
        }

        /// <summary>
        /// Gerencia os eventos que podem partir de um formulário e seus respectivos itens.
        /// </summary>
        /// <param name="aEventInfo">Contém informações sobre o evento.</param>
        /// <returns></returns>
        protected bool HandleMenuEvent( MenuEvent aEventInfo )
        {
            Menu lMenu = AddOn.Menus[aEventInfo.MenuUID];

            if ( lMenu == null )
            {
                return true;
            }

            MethodInfo lMethodInfo = Listener.MenuEvents[aEventInfo.MenuUID, BoEventTypes.et_MENU_CLICK, aEventInfo.BeforeAction];

            if ( lMethodInfo == null )
                return true;

            object lReturn = lMethodInfo.Invoke( lMenu, new[] { aEventInfo } );

            return lMethodInfo.ReturnType == typeof( bool ) ? (bool)lReturn : true;
        }

        #endregion

        #region Implementation of IEventHandler

        /// <summary>
        /// Realiza o tratamento de eventos da aplicação.
        /// </summary>
        /// <param name="aEventType">O tipo do <see cref="BoAppEventTypes">evento</see> disparado pela aplicação.</param>
        public void ApplicationEventHandler( BoAppEventTypes aEventType ){}

        /// <summary>
        /// Realiza o tratamento de eventos de itens de menu da aplicação, especificamente quando ocorre um click sobre eles.
        /// </summary>
        /// <param name="aEventInfo">Contém informações específicas do evento do menu.</param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado devolta para o SAP Business One. Relevante apenas quando <see cref="MenuEvent.BeforeAction"/> for verdadeiro</param>
        public void MenuEventHandler( ref MenuEvent aEventInfo, out bool aKeepEventPropagation )
        {
            aKeepEventPropagation = HandleMenuEvent( aEventInfo );
        }

        /// <summary>
        /// Realiza o tratamento de eventos do formulário quando este adiciona, atualiza, exclui ou carrega dados..
        /// </summary>
        /// <param name="aEventInfo">Contém informações específicas do evento de dados no formulário.
        /// <remarks>
        /// Este parâmetro fornece a propriedade <see cref="BusinessObjectInfo.ObjectKey">ObjectKey</see>, que contém a chave do objeto do formulário.
        /// </remarks>
        /// </param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado devolta para o SAP Business One. Relevante apenas quando <see cref="BusinessObjectInfo.BeforeAction"/> for verdadeiro</param>
        public void FormDataEventHandler( ref BusinessObjectInfo aEventInfo, out bool aKeepEventPropagation )
        {
            aKeepEventPropagation = HandleFormEvent( aEventInfo, "" );
        }

        /// <summary>
        /// Realiza o tratamento de eventos de itens de um formulário (ele próprio incluso).
        /// </summary>
        /// <param name="aFormUniqueId">O código de identificação único para o formulário em questão.</param>
        /// <param name="aEventInfo">Contém informações específicas do evento do item/formulário.</param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado devolta para o SAP Business One. Relevante apenas quando <see cref="ItemEvent.BeforeAction"/> for verdadeiro</param>
        public void ItemEventHandler( string aFormUniqueId, ref ItemEvent aEventInfo, out bool aKeepEventPropagation )
        {
            dynamic lEventInfo = aEventInfo;

            if ( aEventInfo.EventType == BoEventTypes.et_CHOOSE_FROM_LIST )
                lEventInfo = (ChooseFromListEvent) aEventInfo;

            aKeepEventPropagation = HandleFormEvent( lEventInfo );
        }

        /// <summary>
        /// Realiza um tratamento global dos eventos gerados pelo botão direito do mouse.
        /// </summary>
        /// <param name="aEventInfo">Contém informações específicas do evento relacionado ao click do mouse.</param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado devolta para o SAP Business One. Relevante apenas quando <see cref="ContextMenuInfo.BeforeAction"/> for verdadeiro</param>
        public void RightClickEventHandler( ref ContextMenuInfo aEventInfo, out bool aKeepEventPropagation )
        {
            aKeepEventPropagation = HandleFormEvent( aEventInfo );
        }

        /// <summary>
        /// Realiza o tratamento de eventos que indiquem uma tentativa de impressão no sistema.
        /// </summary>
        /// <remarks>
        /// Um evento de impressão ocorre quando:
        /// <list type="bullets">
        /// <item>O usuário clica sobre os icones <value>Print</value> ou <value>Print Preview</value>;</item>
        /// <item>O usuário envia um documento para impressão através da opção de impressão de documentos <value>Document Printing</value></item>
        /// <item>O assistente de geração de documentos da aplicação envia um documento para impressão.</item>
        /// </list>
        /// </remarks>
        /// <remarks>
        /// O evento de impressão ocorre após o evento de seleção do relatório.
        /// </remarks>
        /// <param name="aEventInfo">Contém informações relevantes a impressão do documento.</param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado devolta para o SAP Business One. Relevante apenas quando <see cref="PrintEventInfo.BeforeAction"/> for verdadeiro</param>
        /// <see cref="IEventHandler.ReportDataEventHandler"/>
        public void PrintEventHandler( ref PrintEventInfo aEventInfo, out bool aKeepEventPropagation )
        {
            aKeepEventPropagation = HandleFormEvent( aEventInfo );
        }

        /// <summary>
        /// Realiza o tratamento de eventos que indiquem uma seleção de relatório para impressão.
        /// </summary>
        /// <remarks>
        /// Um evento de impressão ocorre quando:
        /// <list type="bullets">
        /// <item>O usuário clica sobre os icones <value>Print</value> ou <value>Print Preview</value>;</item>
        /// <item>O usuário envia um documento para impressão através da opção de impressão de documentos <value>Document Printing</value></item>
        /// <item>O assistente de geração de documentos da aplicação envia um documento para impressão.</item>
        /// </list>
        /// </remarks>
        /// <remarks>
        /// O evento de seleção do relatório ocorre antes do evento de impressão.
        /// </remarks>
        /// <param name="aEventInfo">Contém informações relevantes a impressão do documento.</param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado devolta para o SAP Business One. Relevante apenas quando <see cref="PrintEventInfo.BeforeAction"/> for verdadeiro</param>
        /// <see cref="IEventHandler.PrintEventHandler"/>
        public void ReportDataEventHandler( ref ReportDataInfo aEventInfo, out bool aKeepEventPropagation )
        {
            aKeepEventPropagation = HandleFormEvent( aEventInfo );
        }

        /// <summary>
        /// Realiza o tratamento de eventos na barra de progresso, disparados quando ela é criada, parada ou liberada.
        /// </summary>
        /// <param name="aEventInfo">Contém informações do evento.</param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado devolta para o SAP Business One. Relevante apenas quando <see cref="ProgressBarEvent.BeforeAction"/> for verdadeiro</param>
        public void ProgressBarEventHandler( ref ProgressBarEvent aEventInfo, out bool aKeepEventPropagation )
        {            
            aKeepEventPropagation = true;
        }

        /// <summary>
        /// Realiza o tratamento de mensagens que são enviadas para a barra de status.
        /// </summary>
        /// <param name="aText">Mensagem de texto enviada para a barra de mensagens.</param>
        /// <param name="aMessageType">O tipo da mensagem.</param>
        public void StatusBarEventHandler( string aText, BoStatusBarMessageType aMessageType ) {}

        /// <summary>
        /// Realiza o tratamento de eventos disparados quando se realiza a impressão ou visualização de um formulário de um Add-On.
        /// </summary>
        /// <remarks>
        /// Permite escolher a chave do layout que será utilizado.
        /// </remarks>
        /// <param name="aEventInfo">Contém informações do evento e permite escolher uma chave para o layout.</param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado devolta para o SAP Business One. Relevante apenas quando <see cref="LayoutKeyInfo.BeforeAction"/> for verdadeiro</param>
        public void LayoutKeyEventHandler( ref LayoutKeyInfo aEventInfo, out bool aKeepEventPropagation )
        {
            aKeepEventPropagation = HandleFormEvent( aEventInfo );
        }

        /// <summary>
        /// Realiza o tratamento de eventos para quando ocorrer a criação de Widgets.
        /// </summary>
        /// <param name="aEventInfo">Contém informações do evento especificas para Widgets.</param>
        /// <param name="aKeepEventPropagation">Indica se o evento deverá ser propagado, executando devidamente o Widget, ou se ele deverá ser cancelado.</param>
        public void WidgetEventHandler( ref WidgetData aEventInfo, out bool aKeepEventPropagation )
        {
            aKeepEventPropagation = true;
        }

        #endregion
    }
}